package com.rookie.tool.module.util.trigger.aop;

import com.rookie.tool.module.util.trigger.annotation.TriggerEvent;
import com.rookie.tool.module.util.trigger.annotation.TriggerParam;
import com.rookie.tool.module.util.trigger.component.EventCallback;
import com.rookie.tool.module.util.trigger.component.TriggerCondition;
import com.rookie.tool.module.util.trigger.component.CloneUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * @author rookie
 */
@Slf4j
@Aspect
@Component
@EnableAspectJAutoProxy
public class TriggerAspect {

    private HashMap<String, Object> beforeMap = new HashMap<>();

    @Pointcut("@annotation(triggerEvent)")
    public void point(TriggerEvent triggerEvent) {
    }

    @Before(value = "point(triggerEvent)", argNames = "joinPoint,triggerEvent")
    public void beforeProcess(JoinPoint joinPoint, TriggerEvent triggerEvent) {
        HashMap<String, Object> map = getValue(joinPoint);
//        map克隆，试了下HashMap的pushAll没用，发现还是会出现浅克隆的情况，所以选用克隆了
        beforeMap = CloneUtils.clone(map);

        Class<? extends TriggerCondition> condition = triggerEvent.condition();
        Class<? extends EventCallback> callback = triggerEvent.callback();
        try {
            TriggerCondition triggerCondition = condition.newInstance();
//            回调赋值
            boolean match = triggerCondition.beforeMatch(map);

            if (match) {
                EventCallback eventCallback = callback.newInstance();
                eventCallback.beforeProcess(map);
            }
        } catch (InstantiationException | IllegalAccessException e) {
            log.error(e.getMessage());
        }
    }


    @After(value = "point(triggerEvent)", argNames = "joinPoint,triggerEvent")
    public void afterProcess(JoinPoint joinPoint, TriggerEvent triggerEvent) {
        HashMap<String, Object> map = getValue(joinPoint);

        Class<? extends TriggerCondition> condition = triggerEvent.condition();
        Class<? extends EventCallback> callback = triggerEvent.callback();
        try {
            TriggerCondition triggerCondition = condition.newInstance();
//            回调赋值
            boolean match = triggerCondition.afterMatch(beforeMap, map);

            if (match) {
                EventCallback eventCallback = callback.newInstance();
                eventCallback.afterProcess(beforeMap,map);
            }
        } catch (InstantiationException | IllegalAccessException e) {
            log.error(e.getMessage());
        }
    }

    private HashMap<String, Object> getValue(JoinPoint joinPoint) {
        //        获取参数值
        Object[] args = joinPoint.getArgs();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//        获取参数名
        String[] parameterNames = signature.getParameterNames();
//        获取参数上的所有注解
        Annotation[][] parameterAnnotations = signature.getMethod().getParameterAnnotations();
//        存取存在注解参数的下标
        List<Integer> hasAnnotation = new ArrayList<>();
        for (int i = 0; i < parameterAnnotations.length; i++) {
            for (Annotation annotation : parameterAnnotations[i]) {
                if (annotation instanceof TriggerParam) {
                    hasAnnotation.add(i);
                    break;
                }
            }
        }
        HashMap<String, Object> map = new HashMap<>(16);
        for (Integer index : hasAnnotation) {
            map.put(parameterNames[index], args[index]);
        }
        return map;
    }

}
